﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace VendingMachine
{
    public enum MessageType
    {
        ERROR,
        ALERT,
        INFO
    }

    public class VendingMachine
    {
        #region Props

        private string                     UserInput             { get; set; }
        private int                        StartColPos           { get; set; }
        private int                        StartRowPos           { get; set; }
        private int                        MsgStartColPos        { get; set; }
        private int                        MsgStartRowPos        { get; set; }
        public MoneyBox                    MoneyBox              { get; set; }
        public VendingMachineStatus        VendingMachineStatus  { get; set; }
        public Dictionary<Product, int>    Products              { get; private set; }
        public ProductLoader               ProductLoader         { get; private set; }

        #endregion

        private const string InsertCoinsMessage = ", Key in amount in pence and press 'Enter': ";

        public VendingMachine()
        {
            MoneyBox = new MoneyBox();
            ProductLoader = new ProductLoader();
            Products = ProductLoader.Products;
        }


        internal void StartUp()
        {
            PrepareMoneyBox();
            EnterVendingLoop();
        }


        public void PrepareMoneyBox()
        {
            MoneyBox.Coins.Add(CoinDenominations.OnePence, 100);
            MoneyBox.Coins.Add(CoinDenominations.FivePence, 102);
            MoneyBox.Coins.Add(CoinDenominations.TenPence, 50);
            MoneyBox.Coins.Add(CoinDenominations.TwentyPence, 50);
            MoneyBox.Coins.Add(CoinDenominations.FiftyPence, 50);
            MoneyBox.Coins.Add(CoinDenominations.OnePound, 50);
            MoneyBox.Coins.Add(CoinDenominations.TwoPound, 10);
        }


        private void SetDisplay()
        {
            Console.Clear();
            Console.Title = "VenCo @ Your Service";
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(ProductLoader.DisplayProducts());
            Console.Write("-->"); StartColPos = Console.CursorLeft; StartRowPos = Console.CursorTop;            
            Console.WriteLine(Environment.NewLine + Environment.NewLine + "Messages:"); MsgStartColPos = Console.CursorLeft; MsgStartRowPos = Console.CursorTop;
            Console.SetCursorPosition(StartColPos, StartRowPos);
        }


        public void EnterVendingLoop()
        {
            SetDisplay();

            while (VendingMachineStatus == VendingMachineStatus.Open)
            {
                try
                {                                     
                    PromptForProductKey();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Oops!?.." + e.StackTrace);
                }
            }
        }


        private void PromptForProductKey()
        {
            string userKeyEntered = Console.ReadLine().Trim();
            int userKey;
            Int32.TryParse(userKeyEntered, out userKey);

            if (Regex.IsMatch(userKeyEntered, @"[^0-9]"))
            {
                SetDisplay();
                SetMessageOnScreen(MessageType.ERROR, "Non numeric value entered");
            }
            if (userKey == 0)
            {
                Console.SetCursorPosition(StartColPos, StartRowPos);
                return;
            }
            else
                ProcessUserInput(userKey);
        }


        private void ProcessUserInput(int userKey)
        {
            int userInputLength;

            if (!IsProductAvailable(userKey))
            {
                SetMessageOnScreen(MessageType.ALERT, "Your Choice: " + userKey + ", is not available, please make another choice.");
                SuspendInput(1);
                SetDisplay();
                return;
            }

            #region Get Amount

            userInputLength = userKey.ToString().Length;
            Product selectedProduct = Products.Where(p => p.Key.InventoryKeyNumber == userKey).FirstOrDefault().Key;

            //Set Curser to End of User Input
            Console.SetCursorPosition(StartColPos + userInputLength, StartRowPos);
            Console.Write(InsertCoinsMessage);
            
            string userPrice = Console.ReadLine().Replace("p", string.Empty);
            decimal userPriceEnt;
            Decimal.TryParse(userPrice, out userPriceEnt);
            if (userPriceEnt == 0) 
            {
                SetMessageOnScreen(MessageType.ALERT, "Invalid amount, please try again.");
                DeleteUserIput(userInputLength);
                //Console.SetCursorPosition(StartColPos + userInputLength + InsertCoinsMessage.Length, StartRowPos);
            }
            else if (userPriceEnt < selectedProduct.Price) 
            {
                SetMessageOnScreen(MessageType.ALERT, "Insufficient amount entered, please try again.");
                DeleteUserIput(userInputLength);
                //Console.SetCursorPosition(StartColPos + userInputLength + InsertCoinsMessage.Length, StartRowPos);
            }

            int j = 3;

            #endregion
        }


        private void DeleteUserIput(int userInputLength)
        {
            for (int i = 0; i < userInputLength; i++)
            {
                Console.Write("\b\b");
            }
        }


        private void SuspendInput(int secs)
        {
            Console.CursorVisible = false;
            Stopwatch s = new Stopwatch();
            s.Start();
            while (s.Elapsed < TimeSpan.FromSeconds(secs))
            {                
                while (Console.KeyAvailable) 
                    Console.ReadKey(true);
                //ConsoleKeyInfo key = Console.ReadKey(true);
            }
            s.Stop();
            Console.CursorVisible = true;
        }
        

        private bool IsProductAvailable(int selectionKey)
        {
            return Products.Select(p => p.Key.InventoryKeyNumber).Distinct().ToList().Contains(selectionKey);
        }        
        
        
        private void SetMessageOnScreen(MessageType messageType, string message)
        {            
            if (messageType == MessageType.ERROR)	        
                Console.ForegroundColor = ConsoleColor.Red;
            else if (messageType == MessageType.ALERT)
                Console.ForegroundColor = ConsoleColor.DarkYellow;
            else
                Console.ForegroundColor = ConsoleColor.Cyan;

            Console.SetCursorPosition(11, StartRowPos + 2);
            Console.WriteLine(message);
            Console.ForegroundColor = ConsoleColor.Cyan;
            //Console.SetCursorPosition(StartColPos, StartRowPos);
        }


        private decimal ProcessAmountEntered()
        {
            return 22.99M;
        }

    }

}